package com.googlecode.restitory.api.servlet;

import java.io.IOException;
import java.util.Stack;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.googlecode.restitory.api.JsonService;
import com.googlecode.restitory.api.gae.DatastoreJsonService;
import com.googlecode.restitory.api.gae.TxJsonService;

public class JsonServiceFilter implements Filter {

	private static final Logger LOG = LoggerFactory
			.getLogger(JsonServiceFilter.class);

	private static ThreadLocal<Stack<JsonService>> handler = new ThreadLocal<Stack<JsonService>>();

	private boolean tx = false;

	private static Class<? extends TxJsonService> service = DatastoreJsonService.class;

	@SuppressWarnings("unchecked")
	@Override
	public void init(FilterConfig config) throws ServletException {
		try {
			tx = "true".equals(config.getInitParameter("tx"));
			String name = config.getInitParameter("service");
			if (name != null) {
				service = (Class<? extends TxJsonService>) Class.forName(name);
			}
			handler.set(new Stack<JsonService>());
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		handler.set(new Stack<JsonService>());
		open(tx);
		try {
			chain.doFilter(request, response);
			commit();
		} finally {
			close();
		}

	}

	@Override
	public void destroy() {
		handler.set(null);
	}

	public static void commit() {
		TxJsonService s = (TxJsonService) get();
		s.commit();
	}

	public static JsonService open(boolean tx) {
		try {
			TxJsonService s = service.newInstance();
			if (tx) {
				s.begin();
			}
			return handler.get().push(s);
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}

	public static void rollback() {
		try {
			TxJsonService s = (TxJsonService) get();
			if (s.isActive()) {
				s.rollback();
			}
		} catch (Exception e) {
			LOG.error("error rolling back", e);
		}
	}

	public static void close() {
		try {
			rollback();
			handler.get().pop();
		} catch (Exception e) {
			LOG.error("error closing", e);
		}
	}

	public static JsonService get() {
		return handler.get().peek();
	}

}
